﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WAU.DAL;
using WAU.DTOs.AppUserModule;
using WAU.DTOs.AppUserModule.Enums;
using WAU.Models;
using WAU.Models.AppUserModels;
using WAU.Models.Framework;
using WAU.Utils;

namespace WAU.Services
{
    public class AppUserService
    {
        private readonly EssentialDbContext dbContext;

        private readonly BaseRepoMysql<AppUser> repoUser;
        private readonly BaseRepoMysql<AppUserRoleREL> repoUserRoleREL;
        private readonly BaseRepoMysql<AppRole> repoRole;
        private readonly BaseRepoMysql<AppUserHierarchy> repoHierarchy;
        private readonly BaseRepoMysql<AppRoleMenuREL> repoRoleMenuREL;
        private readonly BaseRepoMysql<AppMenu> repoMenu;

        private readonly ILogger<AppUserService> logger;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="dbContext"></param>
        /// <param name="logger"></param>
        public AppUserService(EssentialDbContext dbContext, ILogger<AppUserService> logger)
        {
            this.dbContext = dbContext;

            repoUser = new BaseRepoMysql<AppUser>(dbContext);
            repoUserRoleREL = new BaseRepoMysql<AppUserRoleREL>(dbContext);
            repoRole = new BaseRepoMysql<AppRole>(dbContext);
            repoHierarchy = new BaseRepoMysql<AppUserHierarchy>(dbContext);
            repoRoleMenuREL = new BaseRepoMysql<AppRoleMenuREL>(dbContext);
            repoMenu = new BaseRepoMysql<AppMenu>(dbContext);

            this.logger = logger;
        }

        /// <summary>
        /// 判断用户名是否存在
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool IsUserNameExist(string userName)
        {
            // 考虑到有的失效用户可能会恢复，因此查询时不做有效性验证
            return repoUser.Query(a => a.UserName == userName).Any();
        }

        /// <summary>
        /// 创建用户（会将输入的密码重新赋值）
        /// </summary>
        /// <param name="creatorUserId"></param>
        /// <param name="createUserDto"></param>
        /// <param name="roles"></param>
        /// <returns></returns>
        public ExecResult CreateUser(string creatorUserId, CreateUserDto createUserDto, IEnumerable<string> roles = null)
        {
            var query = repoUser.QueryNoTracking(a => a.UserName == createUserDto.UserName);

            if (query.Any())
            {
                return new ExecResult
                {
                    Success = false,
                    ErrorMsg = "用户名重复"
                };
            }

            AppUser appUser = CommonUtils.Map<CreateUserDto, AppUser>(createUserDto);

            appUser.Id = GuidFactory.NewSortedGuid();
            appUser.SecretKey = CommonUtils.GenerateShortUniqueId();
            appUser.Password = PasswordUtils.CreatePassword(appUser.Password, appUser.SecretKey);   // 此处将用户的密码重新赋值
            appUser.Creator = creatorUserId;

            repoUser.Add(appUser);

            // 设置用户层级
            var execResult = SetUserHierarchy(creatorUserId, createUserDto.ParentId, appUser.Id);
            if (!execResult.Success)
            {
                return execResult;
            }

            // 设置用户角色
            if (roles != null)
            {
                var existingUserRoleRELs = repoUserRoleREL.List(a => a.UserId == appUser.Id);
                var toDelRELs = existingUserRoleRELs.Where(a => !roles.Contains(a.RoleId)).ToList();
                var toAddRoles = roles.Except(existingUserRoleRELs.Select(a => a.RoleId))
                                        .Select(roleId => new AppUserRoleREL
                                        {
                                            Id = GuidFactory.NewSortedGuid(),
                                            UserId = appUser.Id,
                                            RoleId = roleId,
                                            Creator = creatorUserId
                                        }).ToList();

                repoUserRoleREL.DeleteMany(creatorUserId, toDelRELs);
                repoUserRoleREL.AddRange(toAddRoles);
            }
            else
            {
                repoUserRoleREL.Add(new AppUserRoleREL
                {
                    Id = GuidFactory.NewSortedGuid(),
                    UserId = appUser.Id,
                    RoleId = "1ec9a57e",    // TODO 默认为管理员角色，便于测试
                    Creator = creatorUserId
                });
            }

            dbContext.SaveChanges();

            return ExecResult.ExecSuccessMsg("注册成功");
        }

        /// <summary>
        /// 新增/编辑角色
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        public ExecResult EditRole(string currentUserId, AppRole role)
        {
            var existingQuery = repoRole.Query(a => a.RoleName == role.RoleName || a.RoleCode == role.RoleCode);

            if (string.IsNullOrEmpty(role.Id))
            {
                if (existingQuery.Any())
                {
                    return new ExecResult { Success = false, ErrorMsg = "角色名称或编码重复" };
                }

                role.Id = GuidFactory.NewSortedGuid();
                role.Creator = currentUserId;
                repoRole.Add(role);
            }
            else
            {
                existingQuery = existingQuery.Where(a => a.Id != role.Id);
                if (existingQuery.Any())
                {
                    return new ExecResult { Success = false, ErrorMsg = "角色名称或编码重复" };
                }

                var entity = repoRole.Find(a => a.Id == role.Id);
                entity = CommonUtils.MapBaseModel(role, entity);
                entity.Modifier = currentUserId;
                repoRole.Update(role);
            }

            return ExecResult.ExecSuccess;
        }

        /// <summary>
        /// 用户角色关系 比较器（用户id 和 角色id 相等则相等）
        /// </summary>
        private class AppUserRoleRELEqualityComparer : IEqualityComparer<AppUserRoleREL>
        {
            public static AppUserRoleRELEqualityComparer Comparer = new AppUserRoleRELEqualityComparer();

            public bool Equals(AppUserRoleREL x, AppUserRoleREL y)
            {
                return x.UserId == y.UserId && x.RoleId == x.RoleId;
            }

            public int GetHashCode(AppUserRoleREL obj)
            {
                return obj.UserId.GetHashCode() + obj.RoleId.GetHashCode();
            }
        }

        /// <summary>
        /// 设置用户的层级
        /// </summary>
        /// <param name="currentOperator">操作人</param>
        /// <param name="parentId"></param>
        /// <param name="appUserId"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        public ExecResult SetUserHierarchy(string currentOperator, string parentId, string appUserId)
        {
            bool isCreate = false;  // 新增 或 编辑

            AppUserHierarchy hierarchy = repoHierarchy.Query(a => a.ParentId == parentId
                                            && a.AppUserId == appUserId).FirstOrDefault();

            // 如果上级 id 为空，则将其置为 null
            if (string.IsNullOrEmpty(parentId))
            {
                parentId = null;
            }

            // 没有层级数据，则为 新增
            if (hierarchy == null)
            {
                isCreate = true;

                hierarchy = new AppUserHierarchy
                {
                    Id = GuidFactory.NewSortedGuid(),
                    ParentId = parentId,
                    AppUserId = appUserId,
                    Creator = currentOperator
                };
            }

            if (parentId != null)
            {
                AppUserHierarchy parentHierarchy = repoHierarchy.Query(a => a.AppUserId == parentId).FirstOrDefault();
                if (parentHierarchy == null)
                {
                    return new ExecResult { Success = false, ErrorMsg = "要设置的上级用户尚未划分层级" };
                }

                hierarchy.Level = (byte)(parentHierarchy.Level + 1);
                hierarchy.HierarchyGraph = parentHierarchy.HierarchyGraph + BaseModel.HIERARCHY_SEPERATOR + appUserId;
            }
            else
            {
                hierarchy.Level = 1;
                hierarchy.HierarchyGraph = appUserId;
            }

            if (isCreate)
            {
                repoHierarchy.Add(hierarchy);
            }
            else
            {
                repoHierarchy.Update(hierarchy);
            }

            return ExecResult.ExecSuccess;
        }

        public class LoginUserInfo
        {
            public string UserId { get; set; }
            public List<string> RoleCodes { get; set; }
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public ExecResult<LoginUserInfo> AppUserLogin(string userName, string password)
        {
            string userId = string.Empty;

            AppUser loginUser = repoUser.Find(a => a.UserName == userName);
            if (loginUser == null)
            {
                string msg = $"用户：{userName} 不存在，登录失败！";
                logger.LogInformation(msg);
                return ExecResult<LoginUserInfo>.Error(msg);
            }

            bool isPasswdValid = PasswordUtils.ValidPassword(loginUser.Password, password, loginUser.SecretKey);
            if (!isPasswdValid)
            {
                string msg = $"用户：{userName} 密码错误（传入值为： {password}），登录失败！";
                logger.LogInformation(msg);
                return ExecResult<LoginUserInfo>.Error(msg);
            }

            userId = loginUser.Id;
            List<string> roleCodes = FetchUserRoles(loginUser.Id);

            if (!roleCodes.Any())
            {
                logger.LogWarning("用户：{0} 登录成功但没有角色，已使用默认角色 {1}", 
                                    userName, GlobalConfig.Setting.DefaultAppUserRoleCode);
                roleCodes = new List<string>() { GlobalConfig.Setting.DefaultAppUserRoleCode };
            }

            LoginUserInfo loginUserInfo = new LoginUserInfo()
            {
                UserId = userId,
                RoleCodes = roleCodes
            };

            return ExecResult<LoginUserInfo>.RetSuccess(loginUserInfo);
        }

        /// <summary>
        /// 获取用户角色
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public List<string> FetchUserRoles(string userId)
        {
            var userRoleQuery = repoUserRoleREL.Query(a => a.UserId == userId);
            var roleQuery = repoRole.Query(a => true);

            List<string> roles = (from a in userRoleQuery
                                   join b in roleQuery on a.RoleId equals b.Id
                                   select b.RoleCode).ToList();
            return roles;
        }

        /// <summary>
        /// 获取用户角色（根据角色编码）
        /// </summary>
        /// <param name="roleCodes"></param>
        /// <returns></returns>
        public List<AppRole> FetchRolesByRoleCodes(IEnumerable<string> roleCodes)
        {
            return repoRole.ListNoTracking(a => roleCodes.Contains(a.RoleCode));
        }


        /// <summary>
        /// 密码工具（将原始密码计算 MD5，加上 SecretKey 后再计算一次）
        /// </summary>
        private class PasswordUtils
        {
            /// <summary>
            /// 创建密码
            /// </summary>  
            /// <param name="origin"></param>
            /// <param name="secretKey"></param>
            /// <returns></returns>
            public static string CreatePassword(string origin, string secretKey)
            {
                string tmp = CommonUtils.CalcMD5(origin);
                return CommonUtils.CalcMD5(tmp + secretKey);
            }

            /// <summary>
            /// 验证密码是否正确
            /// </summary>
            /// <param name="calculatedPasswordStr"></param>
            /// <param name="input"></param>
            /// <param name="secretKey"></param>
            /// <returns></returns>
            public static bool ValidPassword(string calculatedPasswordStr, string input, string secretKey)
            {
                string tmp = CommonUtils.CalcMD5(input);
                return calculatedPasswordStr == CommonUtils.CalcMD5(tmp + secretKey);
            }
        }

        /// <summary>
        /// 添加 用户角色关系
        /// </summary>
        /// <param name="rel"></param>
        /// <returns></returns>
        public ExecResult AddAppUserRoleREL(AppUserRoleREL rel)
        {
            var existingQuery = repoUserRoleREL.QueryNoTracking(a => a.RoleId == rel.RoleId && a.UserId == rel.UserId);
            if (!existingQuery.Any())
            {
                repoUserRoleREL.Add(rel);
            }
            return ExecResult.ExecSuccess;
        }

        /// <summary>
        /// 获取用户
        /// </summary>
        /// <param name="userIds"></param>
        /// <returns></returns>
        public async Task<List<AppUser>> FetchUsers(IEnumerable<string> userIds)
        {
            var userDataList = await repoUser.ListAsyncNoTracking(a => userIds.Contains(a.Id));
            return userDataList;
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<GetAppUserInfoResp> GetAppUserInfo(string userId)
        {
            AppUser appUser = await repoUser.FindAsyncNoTracking(a => a.Id == userId);
            if (appUser == null)
            {
                return null;
            }
            else
            {
                return new GetAppUserInfoResp
                {
                    Id = appUser.Id,
                    UserName = appUser.UserName,
                    RealName = appUser.RealName,
                    HeadImage = appUser.HeadImage,
                    Phone = appUser.Phone,
                    Email = appUser.Email,
                    RegisterTime = appUser.CreateTime.ToString("yyyy/MM/dd HH:mm:ss")
                };
            }
        }

        /// <summary>
        /// 修改用户信息
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="sauiReq"></param>
        /// <param name="newHeadImageUrl"></param>
        /// <returns></returns>
        public async Task<bool> SetAppUserInfo(string currentUserId, SetAppUserInfoReq sauiReq, string newHeadImageUrl)
        {
            AppUser appUser = await repoUser.FindAsync(a => a.Id == currentUserId);
            appUser.RealName = sauiReq.RealName;
            appUser.Phone = sauiReq.Phone;
            appUser.Email = sauiReq.Email;

            if (!string.IsNullOrEmpty(newHeadImageUrl))
            {
                appUser.HeadImage = newHeadImageUrl;
            }

            appUser.Modifier = currentUserId;
            appUser.ModifyTime = DateTime.Now;

            repoUser.Update(appUser);
            repoUser.SaveChanges();

            return true;
        }

        /// <summary>
        /// 修改用户密码
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult<AppUser>> ChangeAppUserPassword(string currentUserId, ChangePasswordDto dto)
        {
            AppUser userEntity = await repoUser.FindAsync(a => a.Id == currentUserId);
            if (userEntity == null)
            {
                return ExecResult<AppUser>.Error("用户不存在");
            }

            bool isPasswdValid = PasswordUtils.ValidPassword(userEntity.Password, dto.OriginPassword, userEntity.SecretKey);
            if (!isPasswdValid)
            {
                return ExecResult<AppUser>.Error("原密码不正确");
            }

            userEntity.Password = PasswordUtils.CreatePassword(dto.NewPassword, userEntity.SecretKey);
            userEntity.Modifier = currentUserId;
            userEntity.ModifyTime = DateTime.Now;

            repoUser.Update(userEntity);
            await repoUser.SaveChangesAsync();
            return ExecResult<AppUser>.RetSuccess(userEntity, "密码修改成功，请重新登录");
        }

        /// <summary>
        /// 判断用户是否存在
        /// </summary>
        /// <param name="userIdentityType"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> IsUserExists(UserIdentityTypeEnum userIdentityType, string value)
        {
            var query = repoUser.QueryNoTracking(a => true);
            if(userIdentityType == UserIdentityTypeEnum.Email)
            {
                query = query.Where(a => a.Email == value);
            }

            return await query.AnyAsync();
        }

        /// <summary>
        /// 重置用户密码
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult<AppUser>> ResetAppUserPassword(string currentUserId, ResetPasswordDto dto)
        {
            AppUser userEntity = await repoUser.FindAsync(a => a.Email == dto.Email);
            if (userEntity == null)
            {
                return ExecResult<AppUser>.Error("用户不存在");
            }

            userEntity.Password = PasswordUtils.CreatePassword(dto.NewPassword, userEntity.SecretKey);
            userEntity.Modifier = currentUserId;
            userEntity.ModifyTime = DateTime.Now;

            repoUser.Update(userEntity);
            await repoUser.SaveChangesAsync();
            return ExecResult<AppUser>.RetSuccess(userEntity, "密码修改成功，请重新登录");
        }
    }
}
